/* === LICENSE INFO ===
 * This source file is subject to the GPLv3 license that is bundled with this
 * package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.gnu.org/licenses/gpl.txt
 * === end of LICENSE INFO === */

/*==============================================================================
  File Name   : LogFileWriter.hpp
  Description : Declaration for LogFileWriter class in GLog module.

  Created on  : 2009-10-03
  Updated on  : 2011-01-30
  Updated by  : Lee Shiou Ming

  Cautions    : (important notes)
  ============================================================================*/

#ifndef LOGFILEWRITER_HPP_
#define LOGFILEWRITER_HPP_

#include "LogWriter.hpp"
#include "GLogDefinition.hpp"
#include <boost/filesystem/operations.hpp>
#include <string>
#include <fstream>


/**
 * @brief All gUtil modules will be wrapped within gUtil namespace.
 */
namespace gUtil
{
/**
 * @brief Namespace for GLog module.
 */
namespace GLog
{
    /**
     * @brief Class implementation for output log to local log file.
     */
    class LogFileWriter : public LogWriter
    {
    public:
        /**
         * @brief Constructor initializes log file name and opens the log file
         *        via file stream.
         *
         * Log messages will be written to the log file
         * <_logFilePath>_<YYYY-MM-DD>.log. If file with that name does not
         * exist, new file will be created instead.
         *
         * @param _logFilePath
         * [IN] Path to the log file.
         * @param _logRotationSize
         * [IN] Log file's rotation size in byte.
         * @param _logRotationInterval
         * [IN] Log file's rotation interval.
         *
         * @throw runtime_error if encounter error in initializing log file.
         */
        LogFileWriter(
                const std::string& _logFilePath,
                const unsigned int _logRotationSize = 0,
                const TimeInterval _logRotationInterval = TIME_INTERVAL_NONE);

        /**
         * @brief Destructor will flush the file stream then close it.
         */
        ~LogFileWriter();

        /**
         * @brief Setter for log file rotation size.
         *
         * @param _logRotationSize
         * [IN] New log file rotation size in byte.
         */
        void setLogRotationSize(unsigned int _logRotationSize);

        /**
         * @brief Setter for log file rotation interval.
         *
         * @param _logRotationInterval
         * [IN] New log file rotation interval.
         */
        void setLogRotationInterval(TimeInterval _logRotationInterval);

        /**
         * @brief Implementation for the same interface inherited from parent
         *        class.
         *
         * Internally uses the default message formatter provided by LogWriter.
         * Each call to this function will flush log message to the log file.
         *
         * @param _logMessage
         * [IN] The message which log client wants to append.
         * @param _logLevelString
         * [IN] Log level string that will be pass to message formatter.
         * @param _logMsgTime
         * [IN] The time when log request was submitted.
         */
        void append(const std::string& _logMessage,
                const std::string& _logLevelString,
                const ptime& _logMsgTime);

    private:
        /**
         * @brief Local file path for the log file.
         */
        boost::filesystem::path m_logFilePath;

        /**
         * @brief Maximum size of each log file in byte.
         *
         * Log file rotation will be disabled if this size is 0; while enabled,
         * log message larger than this size will not be written into log file.
         */
        unsigned int m_logRotationSize;

        /**
         * @brief Time interval of log file rotation.
         */
        TimeInterval m_logRotationInterval;

        /**
         * @brief File stream to be used for output log message to log file.
         */
        std::ofstream m_logFileStream;

        /**
         * @brief Size of active log file, in byte.
         *
         * Calculation of this size could be an overhead, or worse may lead to
         * overflow whenever there is no limit. Hence this field will be updated
         * ONLY IF log file rotation by size is enabled.
         */
        unsigned int m_logFileSize;

        /**
         * @brief Open the log file stream, m_logFileStream, as specified in
         *        path m_logFilePath.
         *
         * @throw runtime_error if failed to open the file stream.
         */
        inline void openLogFile();

        /**
         * @brief Flush and close the log file stream, m_logFileStream, if it is
         *        still opened.
         */
        inline void closeLogFile();

        /**
         * @brief Decide if log file rotation is required now, by checking log
         *        settings and current log file state.
         *
         * @param _logMsgSize
         * [IN] Size (in byte) of the message to be logged.
         * @param _logMsgTime
         * [IN] Request time of the message to be logged.
         *
         * @return
         * - true ==> Need to do log file rotation now.
         * - false ==> Either rotation is disabled, or current state does not
         *             need to do rotation yet.
         */
        inline bool isLogRotationRequired(
                const unsigned int _logMsgSize,
                const GDateTime::ptime& _logMsgTime);

        /**
         * @brief Rotate the log file by appending datetime string to the end
         *        of current log file.
         *
         * For instance, mylog will be rename to mylog.<datetime_string>. Take
         * note that <datetime_string> here represents log file I/O time, hence
         * it could be slightly later than actual log message time. New log
         * messages will be written to a new log file with the same base name,
         * mylog. Before renaming, log file stream will be flush and close if it
         * is still opened. After rotation, stream for new log file will be
         * open, and the record for current log file size will be reset to 0.
         *
         * @throw runtime_error if failed to open stream for new log file.
         */
        void rotateLogFile();

    };  /* end of class LogFileWriter */

}  /* end of namespace GLog */
}  /* end of namespace gUtil */

#endif  /* LOGFILEWRITER_HPP_ */
